package org.dromara.hotel.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.BeanUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.hotel.domain.TerHotelRooms;
import org.dromara.hotel.domain.enums.StaticOrderStatus;
import org.dromara.hotel.domain.vo.TerPayVo;
import org.dromara.hotel.mapper.TerHotelRoomsMapper;
import org.dromara.hotel.service.ITerHotelInventoryService;
import org.dromara.hotel.service.ITerPriceCalendarService;
import org.dromara.hotel.service.IWxMaNoticeService;
import org.dromara.hotel.util.DateUtils;
import org.dromara.tearoom.api.RemoteCouponService;
import org.dromara.tearoom.api.RemotePayService;
import org.dromara.tearoom.api.domain.bo.RemoteHtlOrderBo;
import org.dromara.tearoom.api.domain.bo.RemotePayCommonBo;
import org.dromara.tearoom.api.domain.vo.RemoteCoupVo;
import org.dromara.tearoom.api.domain.vo.RemoteHtlOrderVo;
import org.dromara.tearoom.api.domain.vo.RemotePayVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.dromara.hotel.domain.bo.TerHotelOrdersBo;
import org.dromara.hotel.domain.vo.TerHotelOrdersVo;
import org.dromara.hotel.domain.TerHotelOrders;
import org.dromara.hotel.mapper.TerHotelOrdersMapper;
import org.dromara.hotel.service.ITerHotelOrdersService;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 酒店订单Service业务层处理
 *
 * @author xch
 * @date 2025-06-11
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TerHotelOrdersServiceImpl implements ITerHotelOrdersService {

    private final TerHotelOrdersMapper baseMapper;

    private final TerHotelRoomsMapper roombaseMapper;
    private final ITerPriceCalendarService terPriceCalendarService;
    private final ITerHotelInventoryService hotelInventoryService;
    private final IWxMaNoticeService wxMaNoticeService;
    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);

    @DubboReference
    private RemotePayService remotePayService;


    @DubboReference
    private RemoteCouponService  remoteCouponService;
    /**
     * 查询酒店订单
     *
     * @param id 主键
     * @return 酒店订单
     */
    @Override
    public TerHotelOrdersVo queryById(String id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询酒店订单列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 酒店订单分页列表
     */
    @Override
    public TableDataInfo<TerHotelOrdersVo> queryPageList(TerHotelOrdersBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TerHotelOrders> lqw = buildQueryWrapper(bo);
        Page<TerHotelOrdersVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的酒店订单列表
     *
     * @param bo 查询条件
     * @return 酒店订单列表
     */
    @Override
    public List<TerHotelOrdersVo> queryList(TerHotelOrdersBo bo) {
        LambdaQueryWrapper<TerHotelOrders> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<TerHotelOrders> buildQueryWrapper(TerHotelOrdersBo bo) {
        LambdaQueryWrapper<TerHotelOrders> lqw = Wrappers.lambdaQuery();
        lqw.orderByDesc(TerHotelOrders::getCreateTime);
        lqw.eq(bo.getUserId() != null, TerHotelOrders::getUserId, bo.getUserId());
        lqw.eq(bo.getRoomId() != null, TerHotelOrders::getRoomId, bo.getRoomId());
        lqw.eq(bo.getHotelId() != null, TerHotelOrders::getHotelId, bo.getHotelId());
        lqw.eq(bo.getStartDate() != null, TerHotelOrders::getStartDate, bo.getStartDate());
        lqw.eq(bo.getEndDate() != null, TerHotelOrders::getEndDate, bo.getEndDate());
        lqw.eq(bo.getTotalPrice() != null, TerHotelOrders::getTotalPrice, bo.getTotalPrice());
        lqw.eq(bo.getOrderStatus()!= null, TerHotelOrders::getOrderStatus, bo.getOrderStatus());
        lqw.eq(bo.getPrepayAmount() != null, TerHotelOrders::getPrepayAmount, bo.getPrepayAmount());
        lqw.eq(StringUtils.isNotBlank(bo.getMobile()), TerHotelOrders::getMobile, bo.getMobile());
        lqw.eq(StringUtils.isNotBlank(bo.getPaymentMethod()), TerHotelOrders::getPaymentMethod, bo.getPaymentMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getIsRenew()), TerHotelOrders::getIsRenew, bo.getIsRenew());
        lqw.eq(bo.getCouponPrice() != null, TerHotelOrders::getCouponPrice, bo.getCouponPrice());
        lqw.eq(bo.getCouponHour() != null, TerHotelOrders::getCouponHour, bo.getCouponHour());
        lqw.eq(bo.getRevenue() != null, TerHotelOrders::getRevenue, bo.getRevenue());
        lqw.eq(StringUtils.isNotBlank(bo.getKeyId()), TerHotelOrders::getKeyId, bo.getKeyId());
        lqw.eq(StringUtils.isNotBlank(bo.getDoorKey()), TerHotelOrders::getDoorKey, bo.getDoorKey());
        lqw.eq(StringUtils.isNotBlank(bo.getRenewId()), TerHotelOrders::getRenewId, bo.getRenewId());
        lqw.eq(StringUtils.isNotBlank(bo.getRefundId()), TerHotelOrders::getRefundId, bo.getRefundId());
        return lqw;
    }

    /**
     * 新增酒店订单
     *
     * @param bo 酒店订单
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(TerHotelOrdersBo bo) {
        TerHotelOrders add = BeanUtils.toBean(bo, TerHotelOrders.class);
        validEntityBeforeSave(add);
        add.setId(generateOrderId());
        add.setArrivalTime(StringUtils.isBlank(bo.getArrivalTime())? String.valueOf(LocalDateTime.of(bo.getStartDate(), LocalTime.of(18, 0))) : bo.getStartDate()+" "+bo.getArrivalTime());
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    @Override
    public List<RemoteCoupVo> remoteCoupVoList(TerHotelOrdersBo bo) {
       return remoteCouponService.queryCoupList(LoginHelper.getAppLoginUser().getId(),bo.getTotalPrice());
    }
    /**
     * 修改酒店订单
     *
     * @param bo 酒店订单
     * @return 是否修改成功
     */
    public TerPayVo payOrder(TerHotelOrdersBo bo){
        TerHotelOrders  order= BeanUtils.toBean(bo, TerHotelOrders.class);
        RemotePayVo rpv=  remotePayService.pay(BeanUtils.toBean(bo, RemotePayCommonBo.class));
        TerPayVo payVo=BeanUtils.toBean(rpv, TerPayVo.class);
        if(ObjectUtil.isNotEmpty(payVo)&&payVo.getCode()==HttpStatus.HTTP_OK){
            TerHotelOrders update=new TerHotelOrders();
            update.setId(order.getId());
            update.setPaymentMethod(payVo.getPayMethod());
            update.setCreateTime(LocalDateTime.now());
            update.setUpdateTime(LocalDateTime.now());
            if(bo.getRenewId()!=null){
                TerHotelOrders oldOrder = baseMapper.selectById(bo.getRenewId());
                update.setRenewId(bo.getRenewId());
                update.setRoomNo(oldOrder.getRoomNo());
                update.setIdCardurl(oldOrder.getIdCardurl());
                oldOrder.setId(order.getRenewId());
                oldOrder.setIsRenew("1");
                oldOrder.setUpdateTime(LocalDateTime.now());
                baseMapper.updateById(oldOrder);
            }
            baseMapper.updateById(update);
        }
        payVo.setData(order);
        return payVo;
    }

    @Override
    public Boolean updateByBo(TerHotelOrdersBo bo) {
        if(bo.getCreateTime()!=null){
            bo.setCreateTime(null);
            bo.setUpdateTime(null);
        }
        TerHotelOrders update = MapstructUtils.convert(bo, TerHotelOrders.class);
        if(Objects.equals(bo.getOrderStatus(), StaticOrderStatus.COMPLETED)){
            wxMaNoticeService.cleanOrder(bo.getId());
        }
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TerHotelOrders entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除酒店订单信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TerPayVo createOrder(TerHotelOrdersBo bo) {
        // 1. 验证房型有效性
        TerHotelRooms roomType = roombaseMapper.selectById(bo.getRoomId());
        if(roomType==null){
            throw new ServiceException("房型不存在！");
        }
        // 2. 生成日期范围
        List<LocalDate> dates = DateUtils.getDateRange(
            bo.getStartDate(),
            bo.getEndDate()
        );

        // 3. 原子化库存预扣
        boolean locked = dates.stream().allMatch(date ->
            hotelInventoryService.deductInventory(roomType.getId(), date, 1)
        );

        if (!locked) {
            throw new ServiceException("库存不足");
        }
        updateDbInventory(roomType.getId(), dates);
        bo.setUserId(LoginHelper.getAppLoginUser().getId());
        //更新预计收入
        RemoteHtlOrderVo rvo= remoteCouponService.calculatePrice(BeanUtils.toBean(bo, RemoteHtlOrderVo.class));
        BeanUtils.copyProperties(rvo, bo);
        if(!insertByBo(bo))throw new ServiceException("订单新增失败");
       return payOrder(bo);
    }

    private void updateDbInventory(Long roomTypeId, List<LocalDate> dates) {
        RLock lock = CLIENT.getLock("inventory:db-update:" + roomTypeId);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                dates.forEach(date ->
                    terPriceCalendarService.decreaseAvailable(roomTypeId, date, 1)
                );
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new ServiceException("库存更新冲突");
        } finally {
            lock.unlock();
        }
    }

    public String generateOrderId() {
        SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
        String date = sdf.format(new Date());
        String sequence = String.format("%06d", new Random().nextInt(999999));
        String suffix = RandomStringUtils.randomAlphanumeric(4); // 4位随机字母数字
        return String.format("HTL-%s%s%s", date, sequence, suffix);

    }
    @Override
    public Boolean refund(TerHotelOrdersBo bo){
        String refundId=  remotePayService.refund(BeanUtils.toBean(bo,RemoteHtlOrderBo.class));
        TerHotelOrders update=new TerHotelOrders();
        update.setId(bo.getId());
        update.setOrderStatus(StaticOrderStatus.REFUND);
        update.setRefundId(refundId);
        update.setUpdateTime(LocalDateTime.now());
       return baseMapper.updateById(update)>0;
    }


    @Override
    public TableDataInfo<TerHotelOrdersVo>  queryHotelPage(TerHotelOrdersBo bo, PageQuery pageQuery){
        if(bo.getOrderStatus()==null){
            List<TerHotelOrdersVo> result = baseMapper.selectWechatOrderAllPage(bo.getUserId(),pageQuery.getPageSize(),(pageQuery.getPageNum()-1)*pageQuery.getPageSize());
            return TableDataInfo.build(result);
        }else{
            List<TerHotelOrdersVo> result = baseMapper.selectWechatOrderPage(bo.getOrderStatus(),bo.getUserId(),pageQuery.getPageSize(),(pageQuery.getPageNum()-1)*pageQuery.getPageSize());
            return TableDataInfo.build(result);
        }
    }

}
